import { ExecutionContext, Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthStrategy, PUBLIC_KEY } from '../auth.constant';
import { ExtractJwt } from 'passport-jwt';
import { Reflector } from '@nestjs/core';
import { FastifyRequest } from 'fastify';
import { BusinessException } from '~/common/exceptions/biz.exception';
import { ErrorEnum } from '~/constants/error-code.constant';
import { JwtService } from '@nestjs/jwt';
import { isEmpty, isNil } from 'lodash';
import { TokenService } from '../services/token.service';
interface RequestType {
  Params: {
    uid?: string;
  };
  Querystring: {
    token?: string;
  };
}
@Injectable()
export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) {
  jwtFromRequestFn = ExtractJwt.fromAuthHeaderAsBearerToken();
  @Inject(JwtService)
  private jwtService: JwtService;
  private tokenService: TokenService;
  constructor(private reflector: Reflector) {
    super();
  }
  async canActivate(context: ExecutionContext) {
    const isPublic = this.reflector.getAllAndOverride<boolean>(PUBLIC_KEY, [
      context.getHandler(),
      context.getClass()
    ]);
    console.log(isPublic, 'isPublic');
    if (isPublic) {
      return true;
    }
    let result: boolean = false;
    const request = context.switchToHttp().getRequest<FastifyRequest<RequestType>>();
    const token: string = this.jwtFromRequestFn(request);
    try {
      result = (await super.canActivate(context)) as boolean;
    } catch (err) {
      // 需要后置判断 这样携带了 token 的用户就能够解析到 request.user
      if (isPublic) return true;

      if (isEmpty(token)) throw new UnauthorizedException('未登录');

      // 在 handleRequest 中 user 为 null 时会抛出 UnauthorizedException
      if (err instanceof UnauthorizedException)
        throw new BusinessException(ErrorEnum.INVALID_LOGIN);

      // 判断 token 是否有效且存在, 如果不存在则认证失败
      const isValid = isNil(token) ? undefined : await this.tokenService.checkAccessToken(token);

      if (!isValid) throw new BusinessException(ErrorEnum.INVALID_LOGIN);
    }
    const data = this.jwtService.verify(token);
    request.user = data;
    return result;
  }
}
